#ifndef _CLIENT_H_
#define _CLIENT_H_

#include <cstring>
#include <map>
#include "PracticalSocket.h"

// keyS|idA|, keyLength + idLength < 30. // 32
#define INNER_BUFFER_LENGTH 64
// enc[keyS|idA|idB|nonceA|enc[keyS|idA|]|], idA|idB|nonceA, keyLength * 2 + idLength * 3 + nonceLength < 12 < 121. // 128
#define MAX_BUFFER_LENGTH 128

/*
 * Comaprator for maps.
 */
struct cmp_str {
	bool operator() (char const *a, char const *b) {
		return strcmp(a, b) < 0;
	}
};

/*
 * Class which represents a server.
 */
class Server {
	private:
		char* servHostName;
		int servPort;
		int windowSize;
	public:
		Server(char* , int, int);
		~Server();
		char* getServHostName();
		int getServPort();
		int getWindowSize();
};
/*
 * Server Constructor.
 */
Server::Server(char* hostNameIn, int portIn, int windowSizeIn) {
	servHostName = hostNameIn;
	servPort = portIn;
	windowSize = windowSizeIn;
}
/*
 * Server Destructor.
 */
Server::~Server() {
	delete[] servHostName;
}
/*
 * Server getHostName.
 */
char* Server::getServHostName(){
	return servHostName;
}
/*
 * Server getPort.
 */
int Server::getServPort() {
	return servPort;
}
/*
 * Server getWindowSize.
 */
int Server::getWindowSize() {
	return windowSize;
}

/*
 * Class which represents a client and its connections.
 */
class Client {
	private:
		map<char*, Server*, cmp_str> idServerMap;
		char** serverIDs;
		int index;
		int encryptionOn; // 1 = true.
		char* protocol;
		int sequenceNumberRange;
		int packetSize;
		int timeoutInterval;
		char* kdcHostName;
		int kdcPort;
		char* keyA;
		char* nonceA;
		char* idA;
		int numberOfConnections;
		char* keyS;
		int windowSize;
	public:
		Client(int, char*, int, char*, int, int, int, char*, char*, int, int);
		~Client();
		void addServer(char*, Server*);
		Server* getServer(char*);
		char** getServerIDs();
		int getEncryptionOn();
		char* getProtocol();
		int getSequenceNumberRange();
		int getPacketSize();
		int getTimeoutInterval();
		char* getKdcHostName();
		int getKdcPort();
		char* getKeyA();
		char* getNonceA();
		void setIdA(char*);
		char* getIdA();
		int getNumberOfConnections();
		void setKeyS(char*);
		char* getKeyS();
		int getWindowSize();
};
/*
 * Client Constructor.
 */
Client::Client(int encryptionIn, char* hostNameIn, int portIn, char* protocolIn, int sequenceNumberIn, int packetSizeIn, int timeoutIntervalIn, char* keyIn, char* nonceIn, int numberOfConnectionsIn, int windowSizeIn) {
	encryptionOn = encryptionIn;
	kdcHostName = hostNameIn;
	kdcPort = portIn;
	protocol = protocolIn;
	sequenceNumberRange = sequenceNumberIn;
	packetSize = packetSizeIn;
	timeoutInterval = timeoutIntervalIn;
	keyA = keyIn;
	nonceA = nonceIn;
	numberOfConnections = numberOfConnectionsIn;
	serverIDs = new char*[numberOfConnections];
	windowSize = windowSizeIn;
	index = 0;
}
/*
 * Client Destructor.
 */
Client::~Client() {
	delete[] protocol;
	delete[] kdcHostName;
	delete[] keyA;
	delete[] nonceA;
	delete[] serverIDs;
	delete &idServerMap;
}
/*
 * Client addServer.
 */
void Client::addServer(char* idB, Server* server) {
	idServerMap.insert(pair<char*, Server*>(idB, server));
	serverIDs[index] = idB;
	index++;
}
/*
 * Client getServer.
 */
Server* Client::getServer(char* idB) {
	map<char*, Server*, cmp_str>::iterator iter = idServerMap.find(idB);
	if (iter != idServerMap.end()) {
		return iter->second;
	} else {
		cout << idB << " is not in server map" << endl;
		return 0;
	}
}
/*
 * Client getServerIDs.
 */
char** Client::getServerIDs() {
	return serverIDs;
}
/*
 * Client getEncryptionOn.
 */
int Client::getEncryptionOn() {
	return encryptionOn;
}
/*
 * Client getKdcHostName.
 */
char* Client::getKdcHostName() {
	return kdcHostName;
}
/*
 * Client getKdcPort.
 */
int Client::getKdcPort() {
	return kdcPort;
}
/*
 * Client getProtocol.
 */
char* Client::getProtocol() {
	return protocol;
}
/*
 * Client getSequenceNumberRange.
 */
int Client::getSequenceNumberRange() {
	return sequenceNumberRange;
}
/*
 * Client getPacketSize.
 */
int Client::getPacketSize() {
	return packetSize;
}
/*
 * Client getTimeoutInterval.
 */
int Client::getTimeoutInterval() {
	return timeoutInterval;
}
/*
 * Client getKeyA.
 */
char* Client::getKeyA() {
	return keyA;
}
/*
 * Client getNonceA.
 */
char* Client::getNonceA() {
	return nonceA;
}
/*
 * Client setIdA.
 */
void Client::setIdA(char* idAin) {
	idA = idAin;
}
/*
 * Client getIdA.
 */
char* Client::getIdA() {
	return idA;
}
/*
 * Client getNumberOfConnections.
 */
int Client::getNumberOfConnections() {
	return numberOfConnections;
}
/*
 * Client setKeyS.
 */
void Client::setKeyS(char* keySIn) {
	keyS = keySIn;
}
/*
 * Client getKeyS.
 */
char* Client::getKeyS() {
	return keyS;
}

#endif
